#pragma once
#include <iomanip>
#include "Node.h"

using namespace std;

template < class Key, class Value >
class HashTable
{
		class Iterator
		{
		public:
			typedef Iterator self_type;
			typedef Node<Key,Value> value_type;
			typedef Node<Key,Value>& reference;
			typedef Node<Key,Value>* pointer;
			typedef forward_iterator_tag iterator_category;
			typedef int difference_type;
		
			Iterator(pointer ptr) : ptr_(ptr) { } //Constructor
			Iterator(pointer *dPtr, unsigned int size ) // Other constructor
			{
				this->dPtr = dPtr;
				this->size = size;
				this->ind = 0;
				while( this->dPtr[ind] == nullptr )
				{
					if( this->ind == this->size -1 )
						return;
					ind++;
				}
				this->ptr_ = *this->dPtr;
			}
			self_type operator++() { self_type i = *this; ptr_=ptr_->next; return i; }
			self_type operator++(int junk) 
			{ 
				if( ptr_ == nullptr )
				{
					dPtr++;
					ind++;
					if( ind > size-1 )
						ptr_ = nullptr;
					while( *dPtr == nullptr )
					{
						dPtr++;
						ind++;
						if( ind > size -1 )
							break;
					}
					if( ptr_ != nullptr )
						ptr_ = *dPtr;
				}
				else if ( ptr_->next == nullptr )
				{
					dPtr++;
					ind++;
					if( ind > size-1 )
						ptr_ = nullptr;
					while( *dPtr == nullptr )
					{
						dPtr++;
						ind++;
						if( ind > size -1 )
							break;
					}
					if( ptr_ != nullptr )
						ptr_ = *dPtr;
				}
				else
					ptr_=ptr_->next; 


				return *this; 
			}
			reference operator*() { return *ptr_; }
			pointer operator->() { return ptr_; }
			bool operator==(const self_type& rhs) { return ptr_ == rhs.ptr_; }
			bool operator!=(const self_type& rhs) { return ptr_ != rhs.ptr_; }
		
		private:
			pointer ptr_;
			pointer *dPtr;
			unsigned int size;
			unsigned int ind;
		};

private:
	unsigned int size;
	Node<Key,Value> **dataPtr;
	hash<Key> keyHash;

public:

	HashTable(void)
	{
		size = 0;
		dataPtr = new Node<Key,Value>*[size];
		for( int i = 0; i < this->size; i++ )
		{
			dataPtr[i] = nullptr;
		}
	}

	HashTable( int size )
	{
		this->size = size;
		dataPtr = new Node<Key,Value>*[this->size];
		for( unsigned int i = 0; i < this->size; i++ )
		{
			dataPtr[i] = nullptr;
		}
	}

	~HashTable(void)
	{
		delete[] dataPtr;
	}

	void Add( Key key, Value value )
	{
		unsigned int index = keyHash(key) % this->size;

		if( dataPtr[index] == nullptr )
		{
			dataPtr[index] = new Node<Key,Value>(key,value);
		}
		else
		{
			Node<Key,Value> *temp = new Node<Key,Value>(key,value);

			temp->next = dataPtr[index];
			dataPtr[index] = temp;
		}
	}

	Node<Key,Value>* Search( Key key )
	{
		unsigned int index = keyHash(key) % this->size;

		return dataPtr[index];
	}

	void PrintSpecific( Key key )
	{
		unsigned int index = keyHash(key) % this->size;
		cout << "index: " << index << " Node: ";
		Search( key )->Print();
	}

	void Print()
	{
		
		for( int i = 0; i < this->size; i++ )
		{
			Node<Key,Value>* temp = dataPtr[i];

			cout << dataPtr[i] << endl;

			while( temp != nullptr )
			{
				cout << setw(10) << keyHash(temp->key) % this->size;
				temp->Print();
				temp = temp->next;
			}
		}
	}

	void PrintByIter()
	{

		for( auto iter = begin(); iter != end(); iter++)
		{
			iter->Print();
		}
	}


	Iterator begin()
	{
		return Iterator( dataPtr, this->size );
	}

	Iterator end()
	{
		return nullptr;
	}

};

